/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "dataManager/lottie_config_data_bridge.h"
#include "common/common.h"
#include "dataManager/lottie_config_data.h"
#include "controller/lottie_handler.h"
#include "common/lottile_utils.h"

namespace LottieTurbo {

static const size_t SEGMENT_SIZE = 2;
static const size_t IMGASSET_SIZE = 2;

LottieConfigBridge::LottieConfigBridge(napi_env env, napi_value value) : napiHelper_(NapiHelper(env))
{
    Parse(value);

    napi_value workName;
    napi_create_string_utf8(env, "LottieListener", NAPI_AUTO_LENGTH, &workName);
    napi_create_threadsafe_function(env, nullptr, nullptr, workName, 0, 1, nullptr, nullptr, nullptr,
                                    [](napi_env env, napi_value cb, void *context, void *data) {
        auto trigger = (LottieHandler::TriggerData *)data;
        if (trigger != nullptr) {
            trigger->func();
            delete trigger;
            trigger = nullptr;
        }
    }, &safeFunc_);
    if (listener_ != nullptr) {
        listener_->TriggerEvent(LottieEventType::ON_CONFIG_READY);
    }
}

bool LottieConfigBridge::ConvertToType(napi_value &napiValue, bool &boolValue)
{
    bool isBool = napiHelper_.CheckIsType(napiValue, napi_boolean);
    if (!isBool) {
        return false;
    }
    boolValue = napiHelper_.GetBoolean(napiValue);
    return true;
}

bool LottieConfigBridge::ConvertToType(napi_value &napiValue, std::string &stringValue)
{
    bool isString = napiHelper_.CheckIsType(napiValue, napi_string);
    if (!isString) {
        return false;
    }
    stringValue = napiHelper_.GetString(napiValue);
    return true;
}

bool LottieConfigBridge::ConvertToType(napi_value &napiValue, int &intValue)
{
    bool isNumber = napiHelper_.CheckIsType(napiValue, napi_number);
    if (!isNumber) {
        return false;
    }
    intValue = napiHelper_.GetInteger(napiValue);
    return true;
}

bool LottieConfigBridge::ConvertToType(napi_value &napiValue, double &doubleValue)
{
    bool isDouble = napiHelper_.CheckIsType(napiValue, napi_number);
    if (!isDouble) {
        return false;
    }
    doubleValue = napiHelper_.GetDouble(napiValue);
    return true;
}

bool LottieConfigBridge::ConvertToType(napi_value &napiValue, AnimationSegment &animationSegment)
{
    bool isArray = napiHelper_.CheckIsArray(napiValue);
    if (!isArray) {
        return false;
    }
    auto arrayLength = napiHelper_.GetArrayLength(napiValue);
    if (arrayLength != SEGMENT_SIZE) {
        return false;
    }
    auto initFrame = napiHelper_.GetArrayElement(napiValue, 0);
    auto endFrame = napiHelper_.GetArrayElement(napiValue, 1);
    animationSegment.initFrame = static_cast<float>(napiHelper_.GetDouble(initFrame));
    animationSegment.endFrame = static_cast<float>(napiHelper_.GetDouble(endFrame));
    return true;
}

bool LottieConfigBridge::ConvertToType(napi_value &napiValue, napi_value &jsObject)
{
    bool isObject = napiHelper_.CheckIsType(napiValue, napi_object);
    if (!isObject) {
        return false;
    }
    jsObject = napiValue;
    return true;
}

bool LottieConfigBridge::ConvertToType(napi_value &napiValue, ImageAssetDelegateType &imgAssets)
{
    bool isArray = napiHelper_.CheckIsArray(napiValue);
    if (!isArray || imgAssets == nullptr) {
        return false;
    }
    auto arrayLength = napiHelper_.GetArrayLength(napiValue);
    if (arrayLength <= 0) {
        return false;
    }
    for (int i = 0; i < arrayLength; i++) {
        auto element = napiHelper_.GetArrayElement(napiValue, i);
        auto elementLength = napiHelper_.GetArrayLength(element);
        if (elementLength == IMGASSET_SIZE) {
            auto id = napiHelper_.GetString(napiHelper_.GetArrayElement(element, 0));
            auto fileName = napiHelper_.GetString(napiHelper_.GetArrayElement(element, 1));
            imgAssets->push_back({.refId = std::move(id), .fileName = std::move(fileName)});
        }
    }
    return true;
}

bool LottieConfigBridge::GetResource(napi_value &jsObject, Resource &resource, std::string parameter)
{
    bool isExist = napiHelper_.CheckPropIsExist(jsObject, parameter);
    if (!isExist) {
        return false;
    }
    auto jsPath = napiHelper_.GetObjectProperty(jsObject, parameter);
    bool isObject = napiHelper_.CheckIsType(jsPath, napi_object);
    if (!isObject) {
        return false;
    }

    bool hasId = napiHelper_.CheckPropIsExist(jsPath, "id");
    if (!hasId) {
        return false;
    }
    this->GetConfigValue(jsPath, resource.id, "id");
    this->GetConfigValue(jsPath, resource.bundleName, "bundleName");
    this->GetConfigValue(jsPath, resource.moduleName, "moduleName");
    this->GetConfigValue(jsPath, resource.number, "number");

    bool hasParams = napiHelper_.CheckPropIsExist(jsPath, "params");
    if (!hasParams) {
        return true;
    }
    auto params = napiHelper_.GetObjectProperty(jsPath, "params");
    bool isArray = napiHelper_.CheckIsArray(params);
    if (!isArray) {
        return true;
    }

    auto arrLength = napiHelper_.GetArrayLength(params);
    if (arrLength <= 0) {
        return true;
    }
    auto element = napiHelper_.GetArrayElement(params, 0);
    bool isString = napiHelper_.CheckIsType(element, napi_string);
    if (!isString) {
        return true;
    }
    resource.params = napiHelper_.GetString(element);

    return true;
}

bool LottieConfigBridge::GetConfigLoopValue(napi_value &jsObject, std::variant<bool, int> &value,
                                            std::string const &parameter)
{
    bool isExist = napiHelper_.CheckPropIsExist(jsObject, parameter);
    if (!isExist) {
        return false;
    }
    auto napiValue = napiHelper_.GetObjectProperty(jsObject, parameter);
    bool isBool = napiHelper_.CheckIsType(napiValue, napi_boolean);
    if (isBool) {
        value = napiHelper_.GetBoolean(napiValue);
    } else {
        value = napiHelper_.GetInteger(napiValue);
    }
    return true;
}

bool LottieConfigBridge::GetResourceManager(napi_value &jsObject)
{
    napi_value manager = nullptr;
    if (!GetConfigValue(jsObject, manager, "resManager")) {
        return false;
    }
    auto resManager = OH_ResourceManager_InitNativeResourceManager(napiHelper_.GetEnv(), manager);
    resManager_ = std::shared_ptr<NativeResourceManager>(resManager, OH_ResourceManager_ReleaseNativeResourceManager);
    return resManager_ ? true : false;
}

bool LottieConfigBridge::Parse(napi_value value)
{
    if (!GetConfigLoopValue(value, loop_, "loop")) {
        DRAWING_LOGD("Parse loop error");
    }
    if (!GetConfigValue(value, autoplay_, "autoplay")) {
        DRAWING_LOGD("Parse autoplay error");
    }
    if (!GetConfigValue(value, animationData_, "animationData")) {
        if (!GetConfigValue(value, path_, "path")) {
            if (!GetResource(value, resource_, "path")) {
                DRAWING_LOGE("LottieConfigBridge::Parse: Parse path error");
                return false;
            }
        }
    }
    if (!GetConfigValue(value, initialSegment_, "initialSegment")) {
        DRAWING_LOGD("Parse initialSegment error");
    }
    if (!GetConfigValue(value, lottieId_, "lottieId")) {
        DRAWING_LOGD("Parse lottieId error");
    }
    if (!GetConfigValue(value, contentMode_, "contentMode")) {
        DRAWING_LOGD("Parse contentMode error");
    }
    if (!GetConfigValue(value, frameRate_, "frameRate")) {
        DRAWING_LOGD("Parse frameRate error");
    }
    if (!GetConfigValue(value, imgAssets_, "setImageAssetDelegate")) {
        DRAWING_LOGD("Parse setImageAssetDelegate error");
    }
    if (!GetConfigValue(value, useCache_, "useCache")) {
        DRAWING_LOGD("Parse useCache error");
    }
    if (!GetConfigValue(value, DENSITY_DPI, "density")) {
        DRAWING_LOGD("Parse DENSITY_DPI error");
    }
    if (!GetResourceManager(value)) {
        DRAWING_LOGD("Parse context error");
    }
    napi_value listenerJsObject;
    if (GetConfigValue(value, listenerJsObject, "listener")) {
        if (listener_ == nullptr) {
            listener_ = std::make_unique<LottieListener>(napiHelper_.GetEnv());
        }
        listener_->InitListenerCallback(listenerJsObject);
    } else {
        listener_ = std::make_unique<LottieListener>(nullptr);
    }
    return true;
}

std::unique_ptr<LottieListener> &LottieConfigBridge::GetLottieListener()
{
    return listener_;
}

napi_threadsafe_function LottieConfigBridge::GetSafeFunction()
{
    return safeFunc_;
}
} // namespace LottieC